import ttkbootstrap as ttk
from tkinter import filedialog
import zipfile
import os
import re
import hashlib
import ftplib
import json
from datetime import datetime
import threading
import socket

# 用于控制提取过程是否取消的标志
is_cancelled = False
# 日志文件路径
LOG_FILE_PATH = "processing_log.txt"


def select_zip_file():
    global is_cancelled
    is_cancelled = False
    file_path = filedialog.askopenfilename(filetypes=[("ZIP files", "*.zip")])
    if file_path:
        # 禁用提取按钮，显示取消按钮
        select_button.config(state=ttk.DISABLED)
        cancel_button.pack(pady=10)
        # 显示日志窗口和进度条
        log_text.pack(pady=10)
        progress_bar.pack(pady=10)
        # 在新线程中处理文件，避免阻塞 GUI
        threading.Thread(target=lambda: process_zip_file(file_path)).start()


def cancel_process():
    global is_cancelled
    is_cancelled = True
    # 恢复提取按钮，隐藏取消按钮
    select_button.config(state=ttk.NORMAL)
    cancel_button.pack_forget()
    # 隐藏日志窗口和进度条
    log_text.pack_forget()
    progress_bar.pack_forget()


def update_log(message):
    log_text.insert(ttk.END, message + '\n')
    log_text.see(ttk.END)
    root.update_idletasks()
    # 将日志信息写入本地文件
    with open(LOG_FILE_PATH, 'a', encoding='utf-8') as log_file:
        log_file.write(message + '\n')


def update_progress(percent):
    progress_bar['value'] = percent
    root.update_idletasks()


def show_success_popup():
    popup = ttk.Toplevel(root)
    popup.title("上传成功")
    popup.geometry("200x100")
    popup.overrideredirect(True)  # 去掉窗口边框
    # 计算弹窗显示位置，使其居中
    window_width = root.winfo_width()
    window_height = root.winfo_height()
    x = root.winfo_x() + (window_width - 200) // 2
    y = root.winfo_y() + (window_height - 100) // 2
    popup.geometry(f"+{x}+{y}")

    label = ttk.Label(popup, text="上传成功！", font=("Arial", 16))
    label.pack(pady=30)

    # 3 秒后隐藏弹窗
    popup.after(3000, popup.destroy)


def process_zip_file(file_path):
    global is_cancelled
    try:
        # 解压压缩包
        update_log("开始解压压缩包...")
        extract_dir = "extracted_files"
        with zipfile.ZipFile(file_path, 'r') as zip_ref:
            zip_ref.extractall(extract_dir)
        update_log("压缩包解压完成。")

        # 检查解压后的文件
        update_log("解压后的文件列表:")
        for root_dir, dirs, files in os.walk(extract_dir):
            for file in files:
                if is_cancelled:
                    return
                update_log(os.path.join(root_dir, file))

        # 存储 APK 信息
        apk_info_list = []
        # 存储 URL、域名、IP 信息
        url_info_list = []
        # 存储 collectTargetId、deptCode、collectTime 信息
        collect_target_id = ""
        dept_code = ""
        collect_time = ""

        # 遍历解压后的文件，提取 APK 信息
        update_log("开始提取 APK 信息...")
        for root_dir, dirs, files in os.walk(extract_dir):
            for file in files:
                if is_cancelled:
                    return
                if file.endswith('.apk'):
                    apk_name = file.split('.')[0]
                    apk_filename = file
                    package_name = ""  # 这里假设后续有方式获取包名，暂未实现
                    apk_path = os.path.join(root_dir, apk_filename)
                    apk_info = {
                        "apkname": apk_name,
                        "packagename": package_name,
                        "apkfilename": apk_filename,
                        "apk_path": apk_path  # 添加完整的文件路径
                    }
                    apk_info_list.append(apk_info)
                    update_log(f"找到 APK 文件: {apk_path}")
                    if not os.path.exists(apk_path):
                        update_log(f"警告: 文件 {apk_path} 不存在，跳过该文件。")
                        continue
        update_log("APK 信息提取完成。")

        # 遍历解压后的文件，提取 URL、域名、IP 信息
        update_log("开始提取 URL、域名、IP 信息...")
        for root_dir, dirs, files in os.walk(extract_dir):
            for file in files:
                if is_cancelled:
                    return
                if "820100-0.bcp" in file:
                    file_path = os.path.join(root_dir, file)
                    if os.path.exists(file_path):
                        with open(file_path, 'r', encoding='utf-8') as f:
                            content = f.read()
                            urls = re.findall(r'(https?://[^\s]+|www\.[^\s]+|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})', content)
                            for url in urls:
                                url_info = {
                                    "url": url
                                }
                                url_info_list.append(url_info)
                    else:
                        update_log(f"警告: 文件 {file_path} 不存在，跳过 URL 提取。")
        update_log("URL、域名、IP 信息提取完成。")

        # 遍历解压后的文件，提取 collectTargetId、deptCode、collectTime 信息
        update_log("开始提取 collectTargetId、deptCode、collectTime 信息...")
        for root_dir, dirs, files in os.walk(extract_dir):
            for file in files:
                if is_cancelled:
                    return
                if "010100-0.bcp" in file:
                    file_path = os.path.join(root_dir, file)
                    if os.path.exists(file_path):
                        with open(file_path, 'r', encoding='utf-8') as f:
                            content = f.read()
                            match = re.search(r'34[A-Za-z0-9]+', content)
                            if match:
                                collect_target_id = match.group(0)
                                dept_code = collect_target_id[:12]
                                timestamp_match = re.search(r'17\d{8}', content)
                                if timestamp_match:
                                    timestamp = int(timestamp_match.group(0))
                                    collect_time = datetime.fromtimestamp(timestamp / 1000).strftime('%Y-%m-%d %H:%M:%S')
                    else:
                        update_log(f"警告: 文件 {file_path} 不存在，跳过 collectTargetId 提取。")
        update_log("collectTargetId、deptCode、collectTime 信息提取完成。")

        # 创建 JSON 数据
        update_log("开始创建 JSON 数据...")
        json_data = {
            "collectTargetId": collect_target_id,
            "deptCode": dept_code,
            "collectTime": collect_time,
            "apk": apk_info_list,
            "url": url_info_list
        }
        # 生成 JSON 文件名
        json_filename = f"HYB-{collect_target_id}.json"
        try:
            if is_cancelled:
                return
            with open(json_filename, 'w', encoding='utf-8') as f:
                json.dump(json_data, f, ensure_ascii=False, indent=4)
            update_log(f"JSON 文件 {json_filename} 生成成功")
        except Exception as e:
            update_log(f"生成 JSON 文件时出错: {e}")
        update_log("JSON 数据创建完成。")

        # 生成新的 ZIP 文件名（计算 MD5）
        update_log("开始计算 MD5 值...")
        try:
            if is_cancelled:
                return
            md5 = hashlib.md5(open(file_path, 'rb').read()).hexdigest()
            update_log(f"MD5 值计算成功: {md5}")
        except Exception as e:
            update_log(f"计算 MD5 值时出错: {e}")
        new_zip_filename = f"HYB-{collect_target_id}-{md5}.zip"
        update_log("MD5 值计算完成。")

        # 创建新的 ZIP 压缩包并添加 APK 和 JSON 文件
        update_log("开始创建新的 ZIP 压缩包...")
        try:
            if is_cancelled:
                return
            with zipfile.ZipFile(new_zip_filename, 'w') as zipf:
                for apk_info in apk_info_list:
                    apk_path = apk_info["apk_path"]  # 使用完整的文件路径
                    if os.path.exists(apk_path):
                        zipf.write(apk_path, apk_info["apkfilename"])
                        update_log(f"文件 {apk_path} 已添加到压缩包")
                    else:
                        update_log(f"警告: 文件 {apk_path} 不存在，未添加到压缩包。")
                zipf.write(json_filename, json_filename)
            update_log(f"ZIP 文件 {new_zip_filename} 创建成功")
        except Exception as e:
            update_log(f"创建 ZIP 文件时出错: {e}")
        update_log("新的 ZIP 压缩包创建完成。")

        # 上传新的 ZIP 压缩包到 FTP
        update_log("开始上传新的 ZIP 压缩包到 FTP...")
        ftp_host = "127.0.0.1"  # 替换为实际的 FTP 主机
        ftp_user = "username"  # 替换为实际的 FTP 用户名
        ftp_password = "password"  # 替换为实际的 FTP 密码
        ftp_port = 21  # 替换为实际的 FTP 端口
        try:
            if is_cancelled:
                return
            ftp = ftplib.FTP()
            ftp.connect(ftp_host, ftp_port, timeout=10)  # 设置超时时间为 10 秒
            ftp.login(ftp_user, ftp_password)

            def upload_file():
                try:
                    file_size = os.path.getsize(new_zip_filename)
                    uploaded = 0
                    with open(new_zip_filename, 'rb') as f:
                        def callback(data):
                            nonlocal uploaded
                            uploaded += len(data)
                            percent = (uploaded / file_size) * 100
                            update_progress(percent)

                        ftp.storbinary(f"STOR {new_zip_filename}", f, callback=callback)
                    update_log("上传成功")
                    root.after(3000, show_success_popup)  # 在主线程中显示弹窗
                except Exception as e:
                    if not is_cancelled:
                        update_log(f"上传失败: {e}")
                finally:
                    ftp.quit()

            upload_thread = threading.Thread(target=upload_file)
            upload_thread.start()
            upload_thread.join()
        except ftplib.all_errors as e:
            if not is_cancelled:
                update_log(f"上传失败: {e}")
        update_log("新的 ZIP 压缩包上传完成。")

        # 清理临时文件
        if not is_cancelled:
            update_log("开始清理临时文件...")
            os.remove(json_filename)
            for root_dir, dirs, files in os.walk(extract_dir, topdown=False):
                for file in files:
                    os.remove(os.path.join(root_dir, file))
                for dir in dirs:
                    os.rmdir(os.path.join(root_dir, dir))
            os.rmdir(extract_dir)
            update_log("临时文件清理完成。")

    except Exception as e:
        update_log(f"处理过程中出现错误: {e}")
    finally:
        # 处理完成后，恢复提取按钮，隐藏取消按钮
        select_button.config(state=ttk.NORMAL)
        cancel_button.pack_forget()
        # 隐藏日志窗口和进度条
        log_text.pack_forget()
        progress_bar.pack_forget()


# 创建主窗口
root = ttk.Window(themename="flatly")
root.title("BCP数据包处理客户端")
root.geometry("2000x1000")

# 显示软件名称
title_label = ttk.Label(root, text="黑样本提取上报", font=("Microsoft YaHei", 24, "bold"))
title_label.pack(pady=20)

# 创建选择文件按钮并美化
style = ttk.Style()
style.configure('Custom.TButton',
                font=('Microsoft YaHei', 16),
                foreground='white',
                background='#007BFF',
                borderwidth=0,
                padding=10)
style.map('Custom.TButton',
          background=[('active', '#0056b3'), ('pressed', '#004085')])

select_button = ttk.Button(root, text="一键提取上报", command=select_zip_file, style='Custom.TButton')
select_button.pack(pady=40)

# 创建取消按钮
cancel_button = ttk.Button(root, text="取消", command=cancel_process, style='Custom.TButton')
# 初始时隐藏取消按钮
cancel_button.pack_forget()

# 创建日志显示区域
log_text = ttk.Text(root, bg="#000000", fg="#ffffff", bd=0, insertbackground="#ffffff", height=10, width=60)
log_text.tag_configure("center", justify='center')
log_text.pack_forget()

# 创建进度条
progress_bar = ttk.Progressbar(root, orient="horizontal", length=300, mode="determinate")
progress_bar.pack_forget()

# 显示公司信息，贴着底部并向上一点且居中
root.update_idletasks()
window_width = root.winfo_width()
window_height = root.winfo_height()
company_label = ttk.Label(root, text="技术支持：安徽鹿鼎科技有限公司", font=("Microsoft YaHei", 14))
label_width = company_label.winfo_reqwidth()
y_position = window_height - company_label.winfo_reqheight() - 10
x_position = (window_width - label_width) // 2
company_label.place(x=x_position, y=y_position)

# 运行主循环
root.mainloop()